package com.example.demo.controller;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ludonglue
 * @date 2018/4/18
 */
@Controller
public class FileController {
    private final static Logger logger = LoggerFactory.getLogger(FileController.class);
    private Path rootPath;

    @Autowired
    public FileController(@Value("${file.upload.path}") final String pathStr){
        Path path = Paths.get(pathStr);
        this.rootPath = path;
        if (!Files.exists(path)){
            try {
                Files.createDirectories(path);
            } catch (IOException e) {
                logger.error("Could not initialize storage ", e);
            }
        }
    }

    @RequestMapping(value = "/file", method = RequestMethod.GET)
    public ModelAndView upload(){
        logger.info(">>>>> dispose FileController.index:");
        ModelAndView result = new ModelAndView("/system/upload");
        return result;
    }

    @RequestMapping(value = "/file", method = RequestMethod.POST)
    @ResponseBody
    public String fileUpload(@RequestParam("file") MultipartFile file, @RequestParam("filename")String filename, HttpServletRequest request){
        logger.info("进入上传文件fileUpload方法");
        Map<String, Object> ret = new HashMap<>();
        try {
            if (StringUtils.isEmpty(filename)) {
                filename = StringUtils.cleanPath(file.getOriginalFilename());
            }
            store(file, filename);
            Map<String, Object> data = new HashMap<>();
            ret.put("code", "success");
            data.put("size", file.getSize());
            data.put("url", request.getRequestURL() + "/" + filename);
            ret.put("data", data);
        } catch (Exception e) {
            ret.put("code", "error");
            ret.put("msg", e.getMessage());
            logger.error("Failed to store file " + filename, e);
        }
        return JSON.toJSONString(ret);
    }


    @GetMapping("/file/{filename:.+}")
    @ResponseBody
    public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
        Resource file = loadAsResource(filename);
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=\"" + file.getFilename() + "\"")
                .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream")
                .body(file);
    }

    private void store(MultipartFile file, String filename) throws Exception {
        if (file.isEmpty()) {
            throw new RuntimeException("Failed to store empty file " + filename);
        }
        if (filename.contains("..")) {
            // 安全检查
            throw new RuntimeException("Cannot store file with relative path outside current directory " + filename);
        }
        Files.copy(file.getInputStream(), rootPath.resolve(filename),
                StandardCopyOption.REPLACE_EXISTING);
    }

    private Resource loadAsResource(String filename) {
        try {
            Path file = rootPath.resolve(filename);
            Resource resource = new UrlResource(file.toUri());
            if (resource.exists() || resource.isReadable()) {
                return resource;
            } else {
                throw new RuntimeException("Could not read file: " + filename);
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException("Could not read file: " + filename);
        }
    }

}